import socket
import threading
"""
server = Server(8888)
server.start()
"""
class Server(object):
    def __init__(self,port):
        # 开启一个服务端，服务端需要保存用户的socket信息
        # 以字典的格式缓存，以账号为key  以socket为value
        # 只有用户登录成功
        self.__user_cache={} # 缓存socket
        self.__port = port
        self.__users = {} # 缓存用户的账号和密码信息

    def server_start(self):
        # 创建一个套接字对象 基于TCP协议的
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 给套接字对象绑定一个端口地址，因为这是服务端，服务端需要客户端连接，如果服务端的地址不固定，客户端连接就有问题
        sock.bind(("0.0.0.0",self.__port))
        # 监听连接的，而且一旦socket调用了这个函数，代表这个socket是服务端的socket
        sock.listen(5)
        print(f"======================服务端{self.__port}启动成功=====================")
        # 接受客户端的连接，accept接受一个客户端连接之后，返回一个二元组数据，二元组第一项是服务端创建的用来和当前客户端交互的socket对象 二元组的第二项又是一个二元组（ip，端口）
        # 如果返回值是元组类型的，python支持高级操作，直接用多个参数接受即可
        # accept只能接受一个客户端的连接，接受无数个 accept就得一直执行
        while True:
            client_socket,address = sock.accept()
            print(f">>>>>>>>>>>>>>>有一个客户端连接成功，客户端的ip：{address[0]},端口号为{address[1]}")
            # 缓存起来 目的是为了后期的私聊和群聊
            # 服务端需要接受当前连接的客户端消息，并且处理当前客户端发送的消息，一直接受
            # 如果是当前地址直接写的话，容易造成外层永远无法接受第二个客户端的连接
            # 需要把当前客户端的数据接受和处理创建一个单独的线程去处理
            thread = threading.Thread(target=self.receiver_client_msg,args=(client_socket,address))
            thread.start()

    # 这个函数是用来启动多线程的，用来和某个客户端交互 接受数据
    def receiver_client_msg(self,client_socket,address):
        while True:
            # 接受客户端发送的消息，运行只会接受一次 一次接受5kb 接受的数据是二进制类型
            msg = client_socket.recv(5*1024)
            # 将二进制数据转换为正常的字符串数据
            line = msg.decode("utf-8")
            # 这一行数据可能是私聊的数据，也可能是群聊的数据
            # 私聊数据格式：2,ip,port,msg
            # 群聊格式：1,msg
            li = line.split(",")
            prot = li[0]
            match prot:
                case "1":
                    # 写群聊的功能代码 客户端的传递的群聊消息：1,msg
                    # 首先获取传输的群聊信息
                    qun_msg = li[1]
                    # 获取所有连接的客户端的socket  就是__user_cache中的value集合
                    clis = self.__user_cache.values()
                    print(f"客户端目前连接了：{len(clis)}")
                    for cli in clis:
                        cli.send(f"客户端{address}发送了一个消息：{qun_msg}".encode("utf-8"))
                case "2":
                    # 2,username,msg
                    username = li[1] # 获取客户端传递的私聊用户的IP地址
                    msg = li[2] # 获取客户端传递的私聊信息
                    if  self.__user_cache.get(username) is None:
                        client_socket.send("当前账号不在线".encode("utf-8"))
                    else:
                        self.__user_cache.get(username).send(f"有一个客户端{address}给你发送了一个消息：{msg}".encode("utf-8"))
                case "3":
                    # 用户的注册逻辑，3,username,password
                    # 获取用户名和密码
                    username = li[1]
                    password = li[2]
                    if self.__users.get(username) is None:
                        self.__users[username]=password
                        client_socket.send("3,注册成功".encode("utf-8"))
                    else:
                        client_socket.send("3,注册失败，该用户已存在".encode("utf-8"))
                case "4":
                    # 服务端处理登录逻辑
                    # 4,username,password
                    user = li[1]
                    pwd = li[2]
                    if self.__users.get(user) is None:
                        client_socket.send("4,fail,用户不存在".encode("utf-8"))
                    else:
                        if pwd == self.__users.get(user):
                            # 只有登录成功才会去缓存账号信息
                            self.__user_cache[user] = client_socket
                            client_socket.send("4,ok,登录成功".encode("utf-8"))
                        else:
                            client_socket.send("4,fail,密码不正确".encode("utf-8"))
                case _:
                    # 数据格式错误，给当前发送消息的客户端返回格式错误，
                    client_socket.send("请检查发送的消息，格式错误！".encode("utf-8"))

if __name__ == "__main__":
    server = Server(8889)
    server.server_start()

